//
// file : inet_ipc.h
// date : 06/03/07
//
#ifndef INET_IPC_H_
#define INET_IPC_H_

#include <assert.h>

#if defined (WIN32)

    #pragma warning( disable : 4311 )  // cast void* to int
    #pragma warning( disable : 4312 )  // cast int to void*

    #include "inet_win.h"
    
    #define sio_close       closesocket

    typedef SOCKET          socket_t;
    typedef unsigned long   ulong;

    #define EWOULDBLOCK     WSAEWOULDBLOCK

#elif defined (__unix__)

    #include "inet_nix.h"

    #define __stdcall     
    #define sio_close       close

    typedef int             socket_t;

    #define INVALID_SOCKET  (-1)

#endif


// IN_MULTICAST() takes address in host order
//
#define IS_MULTICAST(a) IN_MULTICAST(ntohl(a))

#include "inet_tmr.h"

typedef long (__stdcall * f_reader)(void *ctx, long len);
typedef long (__stdcall * f_callup)(void *ctx);
typedef long (__stdcall * f_callon)(long  sid);

#define IPC_UDP_IBUF_SIZE (1<<16) // 64 K
#define IPC_UDP_OBUF_SIZE (1<<12) // 4 K

#define IPC_TCP_IBUF_SIZE (1<<18) // 256 K
#define IPC_TCP_OBUF_SIZE (1<<18) // 256 K

#define SOC_FLAG_PERSIST  (1<<0)  // to be persistent
#define SOC_FLAG_PASSIVE  (1<<1)  // tcp/udp serive
#define SOC_FLAG_SPAWNED  (1<<2)  // tcp accepted

#define INVALID_SESSION_  (~0)   // invalid ipc session id

#define IPC_STAT_INVALID  (0<<0)
#define IPC_STAT_SESSION  (1<<0)
#define IPC_STAT_CONNECT  (1<<1)

#define APC_FLAG_DOCLOSE  (1<<0)

#define IPC_TPC_RETRY_MS  (5000)

typedef struct ipccb_t   // ipc context block
{    
    ulong               cbsize;    // ipc block size
	long                sessid;    // session id = (domainid^slotid)
	ulong               nuance[2]; // ipc block id, time, etc

    // socket events 
    int                 sotype;    // SOCK_STREAM, SOCK_DGRAM
    socket_t            socket;    // socket to peer/server socket
    struct sockaddr_in  loaddr;    // local address
    struct sockaddr_in  toaddr;    // peer address
    struct in_addr      mcaddr;    // multicast group
    ulong               sflags;    // socket level flags
    long                tagain;    // retry interval

    // caller specific parameters

    long                states;    // states
    long                obsize;    // outbound buffer size
    long                ocount;    // outbound message byte count
    char               *obound;    // outbound data buffer
    long                ibsize;    // inbound buffer size
    long                ipoint;    // inbound message byte count
    char               *ibound;    // inbound data buffer

    f_reader            reader;    // inbound reader
    f_callup            writer;    // outbound writer
    f_callup            closed;    // called when socket closed inadvertently
    f_callup            tready;    // tcp connected
    f_callup            peeker;    // not in use

	// out-of-thread control
    void               *apcfun;    // user thunk - function entry
	void               *apcsem;    // thunk completion event/semaphore
#define NUM_USER_APCARGS  (4)
	void               *apcarg[NUM_USER_APCARGS]; // user thunk parameters
    long                apcval;    // return value

    ulong               aflags;    // apc level flags

    ulong               uflags;    // user level flags
#define NUM_USER_OBJECTS  (4)
    void               *usrobj[NUM_USER_OBJECTS]; // caller specific
    ulong               uparam[NUM_USER_OBJECTS]; // caller-specified parameter
    ulong               ustate[NUM_USER_OBJECTS]; // caller-specified parameter
#define NUM_BYTES_PEERID  (64)
    char                cbtext[NUM_BYTES_PEERID]; // reports
    
}  ipccb_t;

//
// max number of sessions for a thread domain
//
#ifndef IPCS_PER_DOMAIN
    #define IPCS_PER_DOMAIN  (128)
#endif

//
// max number of active timers per thread
//
#ifndef TIMER_QUEUE_SIZE
    #define TIMER_QUEUE_SIZE (128)
#endif

enum 
{
    e_net_calls = 0,
    e_usr_calls,
    e_tmr_calls,
    e_max_calls,
};


typedef struct ipc_domain_t
{
    long      didx;      // index in global ipc_domain_t array
	ulong     thid;      // thread id
	long      quit;      // set to quit domain thread

    void     *wait[e_max_calls];   // wait objects
    void     *sync;      // exit sync
    void     *qtmr;      // timer queue

    void     *meta;      // platform-specific block

	long      last;      // ipc slot id of last event
    long      ncbs;      // size of ipc array
    ipccb_t  *ipcs[1];   // array of ipc context blocks

}  ipc_domain_t;


#ifdef __cplusplus
extern "C" {
#endif
void  __stdcall ipc_print(char *str, ...);

// internal
long  __stdcall ipc_recving( ipccb_t *ipc );
long  __stdcall ipc_sending( ipccb_t *ipc );
void  __stdcall ipc_closing( ipccb_t *ipc );
long  __stdcall ipc_accept_( ipccb_t *svc );

void* __stdcall get_domain_(long didx);
void* __stdcall ipc_domain_(long sid );
long  __stdcall ipc_initial(long max_sess, long ms_beat);
long  __stdcall ipc_startup(long max_sess, long ms_beat);
void  __stdcall ipc_cleanup(long didx);
long  __stdcall ipc_session(long didx, ulong extra);
void* __stdcall ipc_context(long sid );
long  __stdcall ipc_shutoff(long sid );
long  __stdcall ipc_writing(long sid, void *data, int len);
void* __stdcall ipc_reclaim(long sid);

long  __stdcall set_freader(long sid, f_reader f);
long  __stdcall set_fwriter(long sid, f_callup f);
long  __stdcall set_fpeeker(long sid, f_callup f);
long  __stdcall set_fclosed(long sid, f_callup f);
long  __stdcall set_ftready(long sid, f_callup f);

long  __stdcall set_userobj(long sid, int index, void *obj);
void* __stdcall get_userobj(long sid, int index);
ulong __stdcall xet_u_flags(long sid, ulong flags);
ulong __stdcall xet_s_flags(long sid, ulong flags);
long  __stdcall set_lo_addr(long sid, char *lo_addr_str);
long  __stdcall set_to_addr(long sid, char *to_addr_str);
long  __stdcall set_mc_addr(long sid, char *mc_addr_str);
long  __stdcall set_t_again(long sid, long interval);

// timer 
long  __stdcall ipc_loadtmr(long sid, utimer_t *tmrctx);

// platform specific
long  __stdcall ipc_waitall(ipc_domain_t *domain);
long  __stdcall ipc_osinit_(ipc_domain_t *domain);
long  __stdcall ipc_osexit_(ipc_domain_t *domain);
long  __stdcall ipc_hookup_(ipc_domain_t *domain, ipccb_t *ipc);
long  __stdcall ipc_waitapc(ipc_domain_t *domain, ipccb_t *ipc);
long  __stdcall ipc_asyncpc(long didx, void *func, ...);

void  __stdcall usr_signals(ipc_domain_t *domain );

#ifdef __cplusplus
}
#endif


#endif // INET_IPC_H_
